package com.epoch.base.base;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.epoch.base.constant.Constant;
import com.epoch.base.db.SqlUtils;
import com.epoch.base.export.ExportConstant;
import com.epoch.base.function.BeetlFunctions;
import com.epoch.base.shiro.ShiroKit;
import com.epoch.platform.common.util.DummyRequestForJfinalInjector;
import com.epoch.platform.common.util.I18nUtils;
import com.jfinal.core.Controller;
import com.jfinal.core.NotAction;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Table;
import com.jfinal.plugin.activerecord.TableMapping;

public abstract class BaseController extends Controller{
	
    /**
     * 页面传入所有属性
     *
     * @param modelClass Class
     * @param <T>        model
     * @return model
     */
    @SuppressWarnings("unchecked")
    @NotAction
    public <T> T getModelAll(Class<T> modelClass, String modelName) {
        Map<String, String[]> parasMap = getParaMap();
        Object temp = createInstance(modelClass);
        if (!(temp instanceof Model)) {
            throw new IllegalArgumentException("getModel only support class of Model, using getBean for other class.");
        }
        String modelNameAndDot = StrKit.notBlank(modelName) ? modelName + "." : null;
        Model<?> model = (Model<?>) temp;
        Table table = TableMapping.me().getTable(model.getClass());
        if (table == null) {
            throw new ActiveRecordException("The Table mapping of model: " + modelClass.getName()
                    + " not exists or the ActiveRecordPlugin not start.");
        }
        for (Map.Entry<String, String[]> entry : parasMap.entrySet()) {
            String paraName = entry.getKey();
            String attrName;
            if (modelNameAndDot != null) {
                if (paraName.startsWith(modelNameAndDot)) {
                    attrName = paraName.substring(modelNameAndDot.length());
                } else {
                    continue;
                }
            } else {
                attrName = paraName;
            }

            Class<?> colType = table.getColumnType(attrName);

            try {
                String[] paraValueArray = entry.getValue();
                String paraValue = (paraValueArray != null && paraValueArray.length > 0) ? paraValueArray[0] : null;
                Object value = null;
                if (colType == null) {
                    value = ("".equals(paraValue) ? null : paraValue); // 用户在表单域中没有输入内容时将提交过来 "", 因为没有输入,所以要转成 null.
                    model.put(attrName, value);
                } else {
                    value = paraValue != null ? TypeConverter.convert(colType, paraValue) : null;
                    model.set(attrName, value);
                }
            } catch (Exception e) {

            }
        }

        return (T) model;
    }
	
	@Override
    public void render(String view) {
		//菜单名称回写
        String commonCode = getPara(Constant.MENU_CODE);
        if (StringUtils.isBlank(commonCode)) {
            commonCode = getAttr(Constant.MENU_CODE);
        }
        String menuName = BeetlFunctions.getMenuNameByCode(commonCode);
        if (StringUtils.isNotBlank(commonCode) && StringUtils.isBlank(menuName)) {
            setAttr(Constant.MENU_NAME, commonCode);
        }else{
            setAttr(Constant.MENU_NAME, menuName);
        }
        super.render(view);
        
	}
	
	@Override
    public void renderJson(Object object) {
        if (this.isServerExport()) {
            Object expData = this.getAttr(ExportConstant.EXPORT_DATA);
            if (expData == null) {
                this.setAttr(ExportConstant.EXPORT_DATA, object);
            }
        } else {
            super.renderJson(object);
        }
    }
	
	@NotAction
	protected boolean isServerExport() {
        return Boolean.TRUE.equals(this.getParaToBoolean(ExportConstant.SERVER_EXPORT));
    }
	
	@NotAction
	protected Kv commonParams() {
		Kv param = Kv.create();
		//获取页码数
        Integer pageSize = getParaToInt("pageSize");
        Integer pageNumber = getParaToInt("pageNumber", Integer.valueOf(Constant.INT1));
        if (pageSize == null) {
            Boolean exportAll = this.getParaToBoolean(ExportConstant.NO_PAGINATION);
            if (Boolean.TRUE.equals(exportAll)) {
                pageSize = Integer.valueOf(Integer.MAX_VALUE);
            } else {
                pageSize = Integer.valueOf(Constant.INT10);
            }
        }
        param.set("pageSize", pageSize);
        param.set("pageNumber", pageNumber);
        String paramsString = getPara("filter");
        Map<String, String> fuzzyParams = null;
        if (StringUtils.isNotBlank(paramsString)) {
            @SuppressWarnings("unchecked")
            Map<String, Object> map = JSONObject.parseObject(paramsString, Map.class);
            param.set(map);
            fuzzyParams = SqlUtils.fuzzy(map);
        }
        param.set("_fuzzyParams", fuzzyParams);
        if ("selected".equals(this.getPara("_exportRange"))) {
            String selectIds = StringUtils.trimToEmpty(this.getPara("_selectIds"));
            param.set("_selectIds", selectIds);
        }
        param.set("sortName", getPara("sortName", null));
        param.set("sortOrder", getPara("sortOrder", null));
        String curUserId = ShiroKit.getUserId();
        param.set("_curUserId", curUserId);
        return param;
	}
	
	@NotAction
	protected Kv commonPopupParams() {
        Kv popupParams = this.commonParams();
        String filterJson = this.getPara("_cmnPop_filter");
        String queryParamsStr = this.getPara("filter");
        Map<String, Object> params = this.jsonToMap(queryParamsStr);
        Map<String, Object> filter = this.jsonToMap(filterJson);
        Map<String, String> fuzzyFilter = SqlUtils.fuzzy(filter);
        if (filter != null) {
            popupParams.set(filter);
        }
        popupParams.set("_filter", filter);
        popupParams.set("_params", params);
        popupParams.set("_fuzzyFilter", fuzzyFilter);
        return popupParams;
    }
	
	@NotAction
	protected Map<String, Object> jsonToMap(String jsonStr) {
        Map<String, Object> result = null;
        if (StringUtils.isNotEmpty(jsonStr)) {
            result = JSON.parseObject(jsonStr, Map.class);
        }	
        return result;
    }
	
    /**
     * 获取tableNew编辑数据<br>
     * 此方法将自动从request参数中获取tableNew编辑模式下传回的数据，封装为Map&lt;String, List&lt;E&gt;&gt;对象，其中E为<code>rowType</code>指定类型<br>
     * 返回的map中包含三个映射：<br>
     * <code>newRows</code> - List&lt;E&gt;类型，页面新增的行数据<br>
     * <code>editRows</code> - List&lt;E&gt;类型，页面修改的行数据(根据页面tableNew参数设置可能会包含原有的未修改的行数据)<br>
     * <code>delRows</code> - List&lt;E&gt;类型，页面删除的行数据<br>
     * 对于指定的rowType类型为Model和Java Bean的情况，若request中的参数对应不到model及Java Bean的属性或类型转换出错，则参数将被忽略。<br>
     * 对于指定的rowType类型为Map的情况，所有识别出的参数将全部放入map中，类型全部为String，map实际泛型类型即为Map&lt;String, String&gt;
     *
     * @param rowType 数据行映射类型，可以为Model，Map，Java Bean三大类
     * @return 使用tableNew编辑模式回传的数据封装map
     */
	@NotAction
    public <E> Map<String, List<E>> getTableEditParams(Class<E> rowType) {
        return this.getTableEditParams(rowType, null);
    }

    /**
     * 获取tableNew编辑数据<br>
     * 此方法将自动从request参数中根据指定的dataParamName获取tableNew编辑模式下传回的数据，封装为Map&lt;String, List&lt;E&gt;&gt;对象，其中E为<code>rowType</code>指定类型<br>
     * 返回的map中包含三个映射：<br>
     * <code>newRows</code> - List&lt;E&gt;类型，页面新增的行数据<br>
     * <code>editRows</code> - List&lt;E&gt;类型，页面修改的行数据(根据页面tableNew参数设置可能会包含原有的未修改的行数据)<br>
     * <code>delRows</code> - List&lt;E&gt;类型，页面删除的行数据<br>
     * 对于指定的rowType类型为Model和Java Bean的情况，若request中的参数对应不到model及Java Bean的属性或类型转换出错，则参数将被忽略。<br>
     * 对于指定的rowType类型为Map的情况，所有识别出的参数将全部放入map中，类型全部为String，map实际泛型类型即为Map&lt;String, String&gt;
     *
     * @param rowType       数据行映射类型，可以为Model，Map，Java Bean三大类
     * @param dataParamName request中传入的tableNew编辑数据参数名
     * @return 使用tableNew编辑模式回传的数据封装map
     */
    @NotAction
    public <E> Map<String, List<E>> getTableEditParams(Class<E> rowType, String dataParamName) {
        return this.getTableEditParams(rowType, dataParamName, I18nUtils.I18N_FORM_DATA_SUF);
    }
	
	/**
     * 获取tableNew编辑数据<br>
     * 此方法将自动从request参数中根据指定的dataParamName获取tableNew编辑模式下传回的数据，封装为Map&lt;String, List&lt;E&gt;&gt;对象，其中E为<code>rowType</code>指定类型<br>
     * 根据withI18nFormData参数值将决定是否将可能存在的国际化字段表单数据值封装入对应实体中，仅对<code>rowType</code>为Model或及子类的情况下有效，默认为<code>true</code><br>
     * 返回的map中包含三个映射：<br>
     * <code>newRows</code> - List&lt;E&gt;类型，页面新增的行数据<br>
     * <code>editRows</code> - List&lt;E&gt;类型，页面修改的行数据(根据页面tableNew参数设置可能会包含原有的未修改的行数据)<br>
     * <code>delRows</code> - List&lt;E&gt;类型，页面删除的行数据<br>
     * 对于指定的rowType类型为Model和Java Bean的情况，若request中的参数对应不到model及Java Bean的属性或类型转换出错，则参数将被忽略。<br>
     * 对于指定的rowType类型为Map的情况，所有识别出的参数将全部放入map中，类型全部为String，map实际泛型类型即为Map&lt;String, String&gt;
     *
     * @param rowType         数据行映射类型，可以为Model，Map，Java Bean三大类
     * @param dataParamName   request中传入的tableNew编辑数据参数名
     * @param i18nFormDataSuf 国际化字段表单数据属性名后缀，若不为空，则会封装入国际化字段表单数据
     * @return 使用tableNew编辑模式回传的数据封装map
     */
    @NotAction
    public <E> Map<String, List<E>> getTableEditParams(Class<E> rowType, String dataParamName,
                                                       final String i18nFormDataSuf) {
        final String NEW_ROWS = "newRows";
        final String EDIT_ROWS = "editRows";
        final String DEL_ROWS = "delRows";
        Map<String, List<E>> map = new HashMap<String, List<E>>(3);
        Map<String, Map<Integer, Map<String, String[]>>> cacheMap = new HashMap<String, Map<Integer, Map<String, String[]>>>(
                3);
        cacheMap.put(NEW_ROWS, new HashMap<Integer, Map<String, String[]>>());
        cacheMap.put(EDIT_ROWS, new HashMap<Integer, Map<String, String[]>>());
        cacheMap.put(DEL_ROWS, new HashMap<Integer, Map<String, String[]>>());
        Map<String, String[]> paramMap = this.getParaMap();
        Set<Entry<String, String[]>> eset = paramMap.entrySet();
        Iterator<Entry<String, String[]>> it = eset.iterator();
        while (it.hasNext()) {
            Entry<String, String[]> entry = it.next();
            String name = entry.getKey();
            String[] vals = entry.getValue();
            if (StringUtils.isNotEmpty(name)) {
                String nameSuf = null;
                Map<Integer, Map<String, String[]>> cache = null;
                String newRowsPre = this.getDataParamPre(NEW_ROWS, dataParamName);
                String editRowsPre = this.getDataParamPre(EDIT_ROWS, dataParamName);
                String delRowsPre = this.getDataParamPre(DEL_ROWS, dataParamName);
                if (name.startsWith(newRowsPre)) {
                    nameSuf = name.substring(newRowsPre.length());
                    cache = cacheMap.get(NEW_ROWS);
                } else if (name.startsWith(editRowsPre)) {
                    nameSuf = name.substring(editRowsPre.length());
                    cache = cacheMap.get(EDIT_ROWS);
                } else if (name.startsWith(delRowsPre)) {
                    nameSuf = name.substring(delRowsPre.length());
                    cache = cacheMap.get(DEL_ROWS);
                }
                if (nameSuf != null && cache != null) {
                    int rowIdxEnd = nameSuf.indexOf(']');
                    if (nameSuf.startsWith("[") && rowIdxEnd > 1 && nameSuf.length() > rowIdxEnd + 1) {
                        String rowIdxStr = nameSuf.substring(1, rowIdxEnd);
                        String namePropSuf = nameSuf.substring(rowIdxEnd + 1);
                        Integer rowIdx = null;
                        try {
                            rowIdx = Integer.valueOf(rowIdxStr);
                        } catch (NumberFormatException e) {
                            //LoggerUtil.getLogger().warn("invalid row index for parameter name '{}'", name);
                        }
                        if (rowIdx != null && namePropSuf.startsWith("[") && namePropSuf.endsWith("]")) {
                            String propName = namePropSuf.substring(1, namePropSuf.length() - 1);
                            if (StringUtils.isNotEmpty(propName)) {
                                propName = propName.replace("][", ".");
                                Map<String, String[]> row = cache.get(rowIdx);
                                if (row == null) {
                                    row = new HashMap<String, String[]>();
                                    cache.put(rowIdx, row);
                                }
                                row.put(propName, vals);
                            }
                        }
                    }
                }
            }
        }
        Iterator<Entry<String, Map<Integer, Map<String, String[]>>>> cit = cacheMap.entrySet().iterator();
        while (cit.hasNext()) {
            Entry<String, Map<Integer, Map<String, String[]>>> entry = cit.next();
            String rowsTypeKey = entry.getKey();
            Map<Integer, Map<String, String[]>> rowsMap = entry.getValue();
            List<Integer> keyList = new ArrayList<Integer>(rowsMap.keySet());
            Collections.sort(keyList);
            List<E> rowList = new ArrayList<E>(keyList.size());
            for (Integer rowIdx : keyList) {
                Map<String, String[]> rowData = rowsMap.get(rowIdx);
                E row = this.buildRowObj(rowData, rowType, i18nFormDataSuf);
                rowList.add(row);
            }
            map.put(rowsTypeKey, rowList);
        }
        return map;
    }
    
    @NotAction
    private <E> E buildRowObj(final Map<String, String[]> rowData, Class<E> rowType, final String i18nFormDataSuf) {
        E row = null;
        if (rowData != null && rowType != null) {
            HttpServletRequest dummyReq = new DummyRequestForJfinalInjector(rowData);
            if (Model.class.isAssignableFrom(rowType)) {
                row = ExtInjector.injectModelExt(rowType, null, dummyReq, true);
            } else if (Map.class.isAssignableFrom(rowType)) {
                row = createInstance(rowType);
                @SuppressWarnings("unchecked")
                Map<String, Object> map = (Map<String, Object>) row;
                Iterator<Entry<String, String[]>> it = rowData.entrySet().iterator();
                while (it.hasNext()) {
                    Entry<String, String[]> entry = it.next();
                    String key = entry.getKey();
                    String[] vals = entry.getValue();
                    if (vals != null && vals.length > 0) {
                        map.put(key, vals[0]);
                    }
                }
            } else {
                row = ExtInjector.injectBean(rowType, null, dummyReq, true);
            }
        }
        return row;
    }
    
    @NotAction
    private String getDataParamPre(String targetPre, String dataParamName) {
        String pre = targetPre;
        if (StringUtils.isNotEmpty(targetPre) && StringUtils.isNotEmpty(dataParamName)) {
            pre = dataParamName + "[" + pre + "]";
        }
        return pre;
    }
    
    @NotAction
    private static <T> T createInstance(Class<T> objClass) {
        try {
            return objClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    @NotAction
    public String getParaValuesToStr(String name,String defaultValue){
    	String value = getRequest().getParameter(name);
    	if(StringUtils.isEmpty(value)) {
    		return defaultValue;
    	}
    	return value;
    }
    
    @NotAction
    public String getQueryPara(String name) {
        String value = super.getPara(name);
        if (StringUtils.isNotBlank(value)) {
            setAttr(name, value);
        }
        return value;
    }

    @NotAction
    public String getQueryPara(String name, String defaultValue) {
        String value = super.getPara(name, defaultValue);
        setAttr(name, value);
        return value;
    }
    
    @NotAction
    public String[] getParaValuesToString(String name) {
		String[] values = getRequest().getParameterValues(name);
		if (values == null)
			return null;
		return values;
	}
	
}
